package com.listeningframework.boot.codegen.lang;

import com.listeningframework.boot.codegen.CodeGenConfiguration;
import com.listeningframework.boot.codegen.context.SystemContext;
import com.listeningframework.boot.codegen.exception.BusinessException;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;

class ConfigScannerUtils {

    private ConfigScannerUtils() {
    }

    static final ConfigScannerUtils newinstance() {
        return new ConfigScannerUtils();
    }

    /**
     * 获取codeGen全局参数
     *
     * @throws BusinessException
     */
    Class<?> getGlobalConfig() throws BusinessException {
        String classPackage = SystemContext.getClassPath();
        Set<Class<?>> setClass = getGlobalConfig(classPackage, CodeGenConfiguration.class);
        if (CollectionUtils.isEmpty(setClass) || setClass.size() != 1) {
            throw new BusinessException("CodeGen Config Should Unique");
        }
        Class<? extends Object> configClz = setClass.stream().findFirst().get();
        return configClz;// configClz.getAnnotation(CodeGenConfigure.class);
    }

    private final Set<Class<?>> getGlobalConfig(String classpackage,
                                                Class<? extends Annotation> annotation) throws BusinessException {
        try {
            File rootDir = new File(classpackage);
            if (!rootDir.exists()) {
                return Collections.emptySet();
            }
            if (!rootDir.isDirectory()) {
                return Collections.emptySet();
            }
            if (!rootDir.canRead()) {
                return Collections.emptySet();
            }
            Set<Class<?>> result = new LinkedHashSet<Class<?>>(8);
            getGlobalConfigClass(classpackage, rootDir, new FilenameFilter() {
                @Override
                public boolean accept(File dir, String name) {
                    if (dir.isDirectory()) {
                        return true;
                    }
                    if (StringUtils.endsWith(name, ".class")) {
                        return true;
                    }
                    return false;
                }
            }, annotation, result);
            return result;
        } catch (Exception e) {
            throw new BusinessException("findAnnotated Class Fail", e);
        }
    }

    /**
     * 递归加载文件
     */
    private final void getGlobalConfigClass(String classPackage, File directory, FilenameFilter filter,
                                            Class<? extends Annotation> annotation,
                                            Set<Class<?>> result) throws IOException {
        File[] dirContents = null;
        if (filter == null) {
            dirContents = directory.listFiles();
        } else {
            dirContents = directory.listFiles(filter);
        }
        if (dirContents == null) {
            return;
        }
        Arrays.sort(dirContents);
        for (File content : dirContents) {
            if (content.isDirectory()) {
                getGlobalConfigClass(classPackage, content, filter, annotation, result);
            } else {
                addGlobalConfigClass(classPackage, content, annotation, result);
            }
        }
    }

    private final void addGlobalConfigClass(String classPath, File file, Class<? extends Annotation> annotation,
                                            Set<Class<?>> result) {
        String filepath = file.getAbsolutePath();
        String treatClassPath = classPath.replace("\\", File.separator);
        treatClassPath = treatClassPath.substring(classPath.indexOf(File.separator));
        String treatFilePath = filepath.replace("\\", File.separator);
        treatFilePath = treatFilePath.substring(classPath.indexOf(File.separator));
        treatFilePath = treatFilePath.replace(treatClassPath, "");
        treatFilePath = treatFilePath.substring(0, treatFilePath.lastIndexOf("."));
        treatFilePath = treatFilePath.replace(File.separator, ".");
        Class<?> clz = getClass(treatFilePath);
        if (clz != null && clz.isAnnotationPresent(annotation)) {
            result.add(clz);
        }
    }

    private Class<?> getClass(String className) {
        try {
            return SystemContext.getClassLoader().loadClass(className);
        } catch (ClassNotFoundException e) {
            return null;
        }
    }
}
